<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <style>
        body {
            padding: 0;
            margin: 0;
        }

        .container {
            display: inline-flex;
            flex-direction: column;
            width: 95em;
        }

        .example {
            display: flex;
            align-items: center;
            justify-content: space-between;
            position: relative;
            margin: 4em 5em;
            background-color: #ccc;
        }

        .example > style, .example > noscript > style {
            display: block;
            font-family: monospace;
            margin: 0 3em 0 10em;
        }

        .box {
            display: block;
            width: 3em;
            height: 3em;
            border: solid 1px;
            background-color: palegreen;
        }

        .original {
            position: absolute;
            left: 0;
            top: 0;
            opacity: 20%;
        }
    </style>
</head>
<body>
<p>This is based on the examples for <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/transform-origin">transform-origin</a>
    on MDN</p>
<div class="container">
    <h1>css-transforms-1</h1>
    <div class="example">
        <div>
            <div id="box1" class="box">&nbsp;</div>
            <div class="box original">&nbsp;</div>
        </div>
        <style>#box1 {
            transform: none;
        }</style>
    </div>
    <div class="example">
        <div>
            <div id="box2" class="box">&nbsp;</div>
            <div class="box original">&nbsp;</div>
        </div>
        <style>#box2 {
            transform: scale(1.7);
        }</style>
    </div>
    <div class="example">
        <div>
            <div id="box3" class="box">&nbsp;</div>
            <div class="box original">&nbsp;</div>
        </div>
        <style>#box3 {
            transform: scale(1.7);
            transform-origin: 0 0;
        }</style>
    </div>
    <div class="example">
        <div>
            <div id="box4" class="box">&nbsp;</div>
            <div class="box original">&nbsp;</div>
        </div>
        <style>#box4 {
            transform: scale(1.7);
            transform-origin: 100% -30%;
        }</style>
    </div>
    <div class="example">
        <div>
            <div id="box5" class="box">&nbsp;</div>
            <div class="box original">&nbsp;</div>
        </div>
        <style>#box5 {
            transform: translate(20px, 20px);
        }</style>
    </div>
    <div class="example">
        <div>
            <div id="box6" class="box">&nbsp;</div>
            <div class="box original">&nbsp;</div>
        </div>
        <style>#box6 {
            transform: translate(50%, 10%);
        }</style>
    </div>
    <div class="example">
        <div>
            <div id="box7" class="box">&nbsp;</div>
            <div class="box original">&nbsp;</div>
        </div>
        <style>#box7 {
            transform: rotate(30deg);
        }</style>
    </div>
    <div class="example">
        <div>
            <div id="box8" class="box">&nbsp;</div>
            <div class="box original">&nbsp;</div>
        </div>
        <style>#box8 {
            transform: rotate(30deg);
            transform-origin: 0 0;
        }</style>
    </div>
    <div class="example">
        <div>
            <div id="box9" class="box">&nbsp;</div>
            <div class="box original">&nbsp;</div>
        </div>
        <style>#box9 {
            transform: rotate(30deg);
            transform-origin: 100% 100%;
        }</style>
    </div>
    <div class="example">
        <div>
            <div id="box10" class="box">&nbsp;</div>
            <div class="box original">&nbsp;</div>
        </div>
        <style>#box10 {
            transform: rotate(30deg);
            transform-origin: -1em -3em;
        }</style>
    </div>
    <div class="example">
        <div>
            <div id="box11" class="box">&nbsp;</div>
            <div class="box original">&nbsp;</div>
        </div>
        <style>#box11 {
            transform: skewX(50deg);
            transform-origin: 100% -30%;
        }</style>
    </div>
    <div class="example">
        <div>
            <div id="box12" class="box">&nbsp;</div>
            <div class="box original">&nbsp;</div>
        </div>
        <style>#box12 {
            transform: skewY(50deg);
            transform-origin: 100% -30%;
        }</style>
    </div>
    <h1>css-transforms-2</h1>
    <div class="example">
        <div>
            <div id="box13" class="box">&nbsp;</div>
            <div class="box original">&nbsp;</div>
        </div>
        <style id="box13-style">
        </style>
        <noscript>
            <style>#box13 {
                transform: rotateX(60deg);
                transform-origin: 50% 50%;
            }</style>
        </noscript>
    </div>
    <div class="example">
        <div>
            <div id="box14" class="box">&nbsp;</div>
            <div class="box original">&nbsp;</div>
        </div>
        <style id="box14-style">
        </style>
        <noscript>
            <style>#box14 {
                transform: rotateY(60deg);
                transform-origin: 50% 50%;
            }</style>
        </noscript>
    </div>
    <div class="example">
        <div>
            <div id="box15" class="box">&nbsp;</div>
            <div class="box original">&nbsp;</div>
        </div>
        <style id="box15-style">
        </style>
        <noscript>
            <style>#box15 {
                transform: rotateZ(60deg);
                transform-origin: 50% 50%;
            }</style>
        </noscript>
    </div>
    <div class="example">
        <div>
            <div id="box16" class="box">&nbsp;</div>
            <div class="box original">&nbsp;</div>
        </div>
        <style id="box16-style">
        </style>
        <noscript>
            <style>#box16 {
                transform: rotateX(60deg) rotateY(60deg) rotateZ(60deg);
                transform-origin: 50% 50%;
            }</style>
        </noscript>
    </div>
    <div class="example">
        <div>
            <div id="box17" class="box">&nbsp;</div>
            <div class="box original">&nbsp;</div>
        </div>
        <style id="box17-style">
        </style>
        <noscript>
            <style>#box17 {
                transform: matrix3d(
                    1.00, 0.00, 0.00, 0.00,
                    0.00, 0.50, -0.87, 0.00,
                    0.00, 0.87, 0.50, 0.00,
                    0.00, 0.00, 0.00, 1.00
                );
                transform-origin: 50% 50%;
            }</style>
        </noscript>
    </div>
    <div class="example">
        <div>
            <div id="box18" class="box">&nbsp;</div>
            <div class="box original">&nbsp;</div>
        </div>
        <style id="box18-style">
        </style>
        <noscript>
            <style>#box18 {
                transform: matrix3d(
                    0.50, 0.00, 0.87, 0.00,
                    0.00, 1.00, 0.00, 0.00,
                    -0.87, 0.00, 0.50, 0.00,
                    0.00, 0.00, 0.00, 1.00
                );
                transform-origin: 50% 50%;
            }</style>
        </noscript>
    </div>
    <div class="example">
        <div>
            <div id="box19" class="box">&nbsp;</div>
            <div class="box original">&nbsp;</div>
        </div>
        <style id="box19-style">
        </style>
        <noscript>
            <style>#box19 {
                transform: matrix3d(
                    0.50, -0.87, 0.00, 0.00,
                    0.87, 0.50, 0.00, 0.00,
                    0.00, 0.00, 1.00, 0.00,
                    0.00, 0.00, 0.00, 1.00
                );
                transform-origin: 50% 50%;
            }</style>
        </noscript>
    </div>
    <div class="example">
        <div>
            <div id="box20" class="box">&nbsp;</div>
            <div class="box original">&nbsp;</div>
        </div>
        <style id="box20-style">
        </style>
        <noscript>
            <style>#box20 {
                transform: matrix3d(
                    0.25, -0.43, 0.87, 0.00,
                    0.81, -0.40, -0.43, 0.00,
                    0.53, 0.81, 0.25, 0.00,
                    0.00, 0.00, 0.00, 1.00
                );
                transform-origin: 50% 50%;
            }</style>
        </noscript>
    </div>
    <script>
        "use strict";
        const box13 = document.getElementById("box13");
        const box13Style = document.getElementById("box13-style");

        const box14 = document.getElementById("box14");
        const box14Style = document.getElementById("box14-style");

        const box15 = document.getElementById("box15");
        const box15Style = document.getElementById("box15-style");
        
        const box16 = document.getElementById("box16");
        const box16Style = document.getElementById("box16-style");

        const box17 = document.getElementById("box17");
        const box17Style = document.getElementById("box17-style");

        const box18 = document.getElementById("box18");
        const box18Style = document.getElementById("box18-style");

        const box19 = document.getElementById("box19");
        const box19Style = document.getElementById("box19-style");

        const box20 = document.getElementById("box20");
        const box20Style = document.getElementById("box20-style");

        let rotationAmount = 0;

        function matrixMultiply(left, right) {
            // This assumes left and right are the same size for simplicity.
            const length = left.length;

            const product = [
                [],
                [],
                [],
                [],
            ]

            for (let i = 0; i < length; ++i) {
                for (let j = 0; j < length; ++j) {
                    product[i][j] = left[i][0] * right[0][j] + left[i][1] * right[1][j] + left[i][2] * right[2][j] + left[i][3] * right[3][j];
                }
            }

            return product;
        }

        function rotationMatrix(xAxis, yAxis, zAxis, angle) {
            const radians = angle * (Math.PI / 180);
            const sine = Math.sin(radians);
            const cosine = Math.cos(radians);

            const t = 1 - cosine;
            
            return [
                [t * xAxis * xAxis + cosine, t * xAxis * yAxis - zAxis * sine, t * xAxis * zAxis + yAxis * sine, 0],
                [t * xAxis * yAxis + zAxis * sine, t * yAxis * yAxis + cosine, t * yAxis * zAxis - xAxis * sine, 0],
                [t * xAxis * zAxis - yAxis * sine, t * yAxis * zAxis + xAxis * sine, t * zAxis * zAxis + cosine, 0],
                [0, 0, 0, 1],
            ];
        }

        function setStyle(element, styleElement, transform) {
            styleElement.innerText = `
                #${element.id} { transform: ${transform}; transform-origin: 50% 50%; }
            `
        }

        function matrixRowToString(matrixRow, endWithComma = true) {
            return `${matrixRow[0].toFixed(2)}, ${matrixRow[1].toFixed(2)}, ${matrixRow[2].toFixed(2)}, ${matrixRow[3].toFixed(2)}${endWithComma ? ',' : ''}`;
        }

        function setMatrix3dStyle(element, styleElement, matrix) {
            styleElement.innerText = `#${element.id} { transform: matrix3d(${matrixRowToString(matrix[0])} ${matrixRowToString(matrix[1])} ${matrixRowToString(matrix[2])} ${matrixRowToString(matrix[3], false)}); transform-origin: 50% 50%; }`;
        }

        function animateCssTransformsTwo() {
            rotationAmount++;
            rotationAmount %= 360;

            setStyle(box13, box13Style, `rotateX(${rotationAmount}deg)`);
            setStyle(box14, box14Style, `rotateY(${rotationAmount}deg)`);
            setStyle(box15, box15Style, `rotateZ(${rotationAmount}deg)`);
            setStyle(box16, box16Style, `rotateX(${rotationAmount}deg) rotateY(${rotationAmount}deg) rotateZ(${rotationAmount}deg)`);

            const xRotationMatrix = rotationMatrix(1, 0, 0, rotationAmount);
            const yRotationMatrix = rotationMatrix(0, 1, 0, rotationAmount);
            const zRotationMatrix = rotationMatrix(0, 0, 1, rotationAmount);

            setMatrix3dStyle(box17, box17Style, xRotationMatrix);
            setMatrix3dStyle(box18, box18Style, yRotationMatrix);
            setMatrix3dStyle(box19, box19Style, zRotationMatrix);
            setMatrix3dStyle(box20, box20Style, matrixMultiply(matrixMultiply(xRotationMatrix, yRotationMatrix), zRotationMatrix));

            requestAnimationFrame(animateCssTransformsTwo);
        }

        requestAnimationFrame(animateCssTransformsTwo);

        const xRotationMatrix = rotationMatrix(1, 0, 0, 60);
        const yRotationMatrix = rotationMatrix(0, 1, 0, 60);
        const zRotationMatrix = rotationMatrix(0, 0, 1, 60);
    </script>
</div>
</body>
</html>
